/*
 * IRremote
 * Version 0.11 August, 2009
 * Copyright 2009 Ken Shirriff
 * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
 *
 * Interrupt code based on NECIRrcv by Joe Knapp
 * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556
 * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
 */

#include "Arduino.h"

#include "IRjam.h"
#include "IRsend.h"
#include "IRremoteInt.h"

// Provides ISR
#include <avr/interrupt.h>

volatile irparams_t irparams;
IRjam irJam(2);

// NEC state machine
volatile uint8_t nec_state;
uint32_t nec_data; // to store data sent
uint8_t bits_received; // count received bits
IRsend irsend; // sender to jam
uint8_t offset = 0;

uint8_t filter_address;
uint8_t filter_command;

uint8_t data_bit_counter = 0;

uint8_t decide_NEC_Jam(uint32_t data) {

    if (++data_bit_counter == 32) {
        data_bit_counter = 0;
        return ((data & 0xFF) == filter_command);
    }
    if (data_bit_counter < 16 && filter_address == 0 && filter_command == 0) return 1;
        
    if(data_bit_counter == 16 && filter_address != 0) {
        return = (data == filter_address);
    }
    
    if(data_bit_counter == 24 && filter_command !=0 ) {
        return = ((data & 0xFF) == filter_command);
    }
    return 0;
}

void jamNECFrame() {
    irsend.sendNEC(0xFFFFFFFF, 32);
}

IRjam::IRjam(int recvpin)
{
  irparams.recvpin = recvpin;
  irparams.blinkflag = 0;
}

// initialization
void IRjam::start() {
  // setup pulse clock timer interrupt
  TCCR2A = 0;  // normal mode

  //Prescale /8 (16M/8 = 0.5 microseconds per tick)
  // Therefore, the timer interval can range from 0.5 to 128 microseconds
  // depending on the reset value (255 to 0)
  cbi(TCCR2B,CS22);
  sbi(TCCR2B,CS21);
  cbi(TCCR2B,CS20);

  //Timer2 Overflow Interrupt Enable
  sbi(TIMSK2,TOIE2);

  RESET_TIMER2;

  sei();  // enable interrupts

  // initialize state machine variables
  irparams.rcvstate = STATE_IDLE;
  irparams.rawlen = 0;


  // set pin modes
  pinMode(irparams.recvpin, INPUT);
}

// TIMER2 interrupt code to collect raw data.
// Widths of alternating SPACE, MARK are recorded in rawbuf.
// Recorded in ticks of 50 microseconds.
// rawlen counts the number of entries recorded so far.
// First entry is the SPACE between transmissions.
// As soon as a SPACE gets long, ready is set, state switches to IDLE, timing of SPACE continues.
// As soon as first MARK arrives, gap width is recorded, ready is cleared, and new logging starts
ISR(TIMER2_OVF_vect)
{
  RESET_TIMER2;

  uint8_t irdata = (uint8_t)digitalRead(irparams.recvpin);
  
  irparams.timer++; // One more 50us tick

  if (offset >= RAWBUF) {
    offset = 0;
    /*
    for (int i=0; i<RAWBUF; i++) {
        Serial.print(irparams.rawbuf[i]);
        Serial.print(" ");
    }*/
  }
  
  switch(irparams.rcvstate) {
  case STATE_IDLE: // In the middle of a gap
    if (irdata == MARK) {
      if (irparams.timer < GAP_TICKS) {
        // Not big enough to be a gap.
        irparams.timer = 0;
      } 
      else {
        // gap just ended, record duration and start recording transmission
        irparams.rawlen = 0;
        irparams.timer = 0;
        irparams.rcvstate = STATE_MARK;
      }
    }
    break;
  case STATE_MARK: // timing MARK
    if (irdata == SPACE) {   // MARK ended, record time
      if(MATCH_MARK(irparams.timer, NEC_HDR_MARK)) { // always reset on long frame
        irparams.rawbuf[offset++] = NEC_STATE_HDR_MARK;
        nec_state = NEC_STATE_HDR_MARK;
        nec_data = 0; // reset data
      } else { //if (MATCH_MARK(irparams.timer, NEC_BIT_MARK)) {
        irparams.rawbuf[offset++] = NEC_STATE_WAIT_SPACE;
        nec_state = NEC_STATE_WAIT_SPACE;
      } 
      /*else {
        irparams.rawbuf[offset++] = irparams.timer;
        nec_state = NEC_STATE_IDLE;
        //nec_data = 0;
        irparams.rcvstate = STATE_IDLE;
      }*/
      irparams.timer = 0;
      irparams.rcvstate = STATE_SPACE;
      
    }
    break;
  case STATE_SPACE: // timing SPACE
    if (irdata == MARK) { // SPACE just ended, record it
      if((nec_state == NEC_STATE_HDR_MARK) && MATCH_SPACE(irparams.timer, NEC_HDR_SPACE)) {
          irparams.rawbuf[offset++] = NEC_STATE_ACCEPT;
          nec_state = NEC_STATE_ACCEPT;
      } 
      if(nec_state == NEC_STATE_WAIT_SPACE) {
          if(MATCH_SPACE(irparams.timer, NEC_ZERO_SPACE)) {
            nec_data <<= 1;
            if (decide_NEC_Jam(nec_data)) {
                irsend.sendNEC(0xFFFFFFFF, 32);
                Serial.println("JAMMMMMMM");
                nec_data = 0;
                data_bit_counter = 0;
                irJam.start();
                return;  
            }
            irparams.rawbuf[offset++] = NEC_STATE_ACCEPT;
            nec_state = NEC_STATE_ACCEPT;
          } else {//if(MATCH_SPACE(irparams.timer, NEC_ONE_SPACE)) {
            nec_data = (nec_data << 1) | 1;
            if (decide_NEC_Jam(nec_data)) {
                irsend.sendNEC(0xFFFFFFFF, 32);
                Serial.println("JAMMMMMMM");            
                nec_data = 0;
                data_bit_counter = 0;
                irJam.start();
                return;
            }
            irparams.rawbuf[offset++] = NEC_STATE_ACCEPT;
            nec_state = NEC_STATE_ACCEPT;
          } 
          /*else { // space is too big => reinitialize
            irparams.rawbuf[offset++] = 3;
            nec_state = NEC_STATE_IDLE;
            nec_data = 0;
          }*/
      }
      irparams.timer = 0;
      irparams.rcvstate = STATE_MARK;
    } 
    else { // SPACE
      if (irparams.timer > (1.3*NEC_HDR_SPACE/USECPERTICK+1)) {
        /* gap longer than longest gap in NEC protocol
         * so we reinitialize */
        //decide_NEC_Jam(nec_data);
        irparams.timer = 0;
        irparams.rawbuf[offset++] = 1;
        irparams.rcvstate = STATE_IDLE;
        nec_data = 0;
      } 
    }
    break;
  }
}

void IRjam::resume() {
  irparams.rcvstate = STATE_IDLE;
  irparams.rawlen = 0;
}

/**
 * set a filter for jamming
 * if a frame matches the filter the frame will be destroyed
 * if one of the parameters is 0, it will be ignored, i.e., all
 * frames will match the filter.
 * Address and Command will not take bit rversing into account!!
 * Bits are processed like they arrive over the air
 */
void IRjam::set_filter_NEC(uint8_t address, uint8_t command)
{
    filter_address = address;
    filter_command = command;
}
